home *** CD-ROM | disk | FTP | other *** search
- /*
-
-
- Copyright (C) 1990 Texas Instruments Incorporated.
-
- Permission is granted to any individual or institution to use, copy, modify,
- and distribute this software, provided that this complete copyright and
- permission notice is maintained, intact, in all copies and supporting
- documentation.
-
- Texas Instruments Incorporated provides this software "as is" without
- express or implied warranty.
-
-
- *
- * Edit history
- * Created: LGO 30-Mar-89 -- Initial design and implementation.
- *
- * The CLASS macro
- *
- * The #pragma defmacro class call will pass arbitrary parameters to the
- * defmacro. We use this mechanism to tell the class macro to stick
- * macro calls into the class definition. In this way the class macro
- * doesn't generate code itself, all code is defined in user modifiable
- * header files. The macros will be called with the class name and the
- * base-class name. When slots or methods are specified a BODY argument
- * is provided with a macro call for each slot or method. The slot macros
- * are passed the slot type and name, and the method macros are passed the
- * result type, name and arglist. The arglist is passed in double quotes,
- * because of the embedded commas (see the #~ MACRO operator).
- *
- * There is a need to select from categories of things within the class:
- *
- * slots methods // Only one may be specified
- * virtual inline normal friend static
- * // if none specified, use virtual inline normal
- * private protected public // if none specified, all slots or methods used
- * inside outside // where to expand the macro call
- *
- * These are specfied with the classmac defmacro with
- * the following grammar:
- * classmac(macro_name, :REST keywords)
- * where keywords is zero or more of
- * inside | slots | methods | virtual | inline | normal |
- * private | protected | public
- *
- * When a keyword has a value, the value is the name of a macro to call
- * when the preceding keywords apply. The macros will be expanded outside
- * and after the class definition, unless the "inside" keyword is found
- * anywhere in the parameter list. In this case the macro is expanded
- * inside the class definition, at the end.
- *
- * Example:
- *
- * #pragma defmacro class "class"
- * #pragma defmacro classmac "classmac" delimiter=)
- * classmac(DECLARE_Generic, inside)
- * classmac(IMPLEMENT_Generic)
- * classmac(generate_map_over_slots, slots=generate_map_slot)
- * classmac(my_generate_methods, methods, public=public_methods, \
- * methods, protected, private=misc_methods)
- *
- * struct method_entry {
- * Symbol* name;
- * Symbol* type;
- * char* args;
- * Boolean is_public;
- * };
- *
- * // Invoked from the class macro
- * MACRO my_generate_methods(class_name, base_class, BODY: methods) {
- * method_entry class_name##_method_table[] = { methods }; }
- *
- * // expanded within my_generate_methods
- * MACRO public_methods(type, name, args) {
- * {SYM(name), SYM(type), args, TRUE} }
- *
- * MACRO misc_methods(type, name, args) {
- * {SYM(name), SYM(type), args, FALSE} }
- *
- * MACRO generate_map_over_slots(class_name, base_class, BODY: slots) {
- * Boolean class_name::map_over_slots(Slot_Mapper procedure, void* rock=NULL){
- * return base_class::map_over_slots(procedure rock) slots;
- * }
- * }
- *
- * MACRO generate_map_slot(type, name, value) {
- * || ((*procedure)(this, #name, &name, SYM(type), rock)) }
- * protected:
- * virtual Symbol** type_list();
- * public:
- * Long(long value) { num = value; }; // Make a long
- * long operator long() { return this->num; }; // Get the value out
- * virtual Boolean map_over_slots(Slot_Mapper procedure, void* rock = NULL);
- * };
- *
- * extern Symbol* Generic_types[];
- * Symbol* Long_types[] = {sym(Long), (Symbol*) Generic_types, NULL};
- * Symbol** Long::type_list() {return Long_types;};
- *
- * Boolean Long::map_over_slots (Slot_Mapper procedure, void* rock = NULL) {
- * return (
- * Object::map_over_slots(procedure rock) // Do inherited slots first
- * || ((procedure)(this, "num", &num, SYMBOL("long"), rock));
- * )
- * }
- *
- */
-
- #include "defmacio.h"
- #include "macro.h"
-
- static char* classkeys[] =
- {"slots", "methods", "virtual", "inline", "normal", "friend", "static",
- "private", "protected", "public", "inside", NULL};
-
- #define s_slots 0x001
- #define s_methods 0x002
- #define s_virtual 0x004
- #define s_inline 0x008
- #define s_normal 0x010
- #define s_friend 0x020
- #define s_static 0x040
- #define s_private 0x080
- #define s_protected 0x100
- #define s_public 0x200
- #define s_inside 0x400
-
- /*
- * There's a CMAC for every classmac encountered. The first one is pointed
- * to by Cmac_head. Additional classmac's are added at Cmac_tail.
- * There's a CBODY for every macro in the body of a CMAC.
- */
- typedef struct Cbody {
- struct Cbody* next; /* Next macro or NULL */
- char* name; /* macro name */
- unsigned int specifiers; /* specifier bit set */
- } CBODY;
-
- typedef struct Cmac {
- struct Cmac* next; /* Next macro or NULL */
- struct Cbody* body; /* List of body macro spec's */
- char* macro; /* macro name */
- int is_inside; /* True when inside, else outside */
- } CMAC;
-
- static CMAC* Cmac_head = NULL; /* First class macro */
- static CMAC* Cmac_tail = NULL; /* Last class macro */
-
- #define BSIZE 512
- /*
- * Return the mask for a class keyword, else zero
- */
- static int class_key(arg)
- char* arg;
- {
- char** key = classkeys;
- int type = 1;
- while (*key != NULL) { /* Find keyword */
- if (strcmp(*key, arg) == 0) break;
- key++;
- type = type << 1;
- }
- if (*key == NULL)
- return 0;
- else
- return type;
- }
-
- /*
- * Parse the arguments to the class macro
- */
- int classmac(argc, argv)
- int argc;
- char* argv[];
- {
- Arg* args = NULL;
- Arg* argp;
- char macname[BSIZE];
- if(copytoken(macname) == NULL) /* get macro name */
- return(1);
- if((args = macro_args(macname)) == &arg_error) /* Gather arguments */
- return(1);
- {
- int mactype = 0;
- CMAC* mac = (CMAC*) getmem(sizeof(CMAC));
- CBODY** next = &mac->body;
- mac->body = NULL;
- mac->macro = args->name; /* First parameter is macro name */
- mac->is_inside = FALSE;
- mac->next = NULL;
- if (Cmac_head == NULL) { /* Link in first macro */
- Cmac_head = mac;
- Cmac_tail = mac;
- } else { /* Link in next macro */
- Cmac_tail->next = mac;
- Cmac_tail = mac;
- }
- /* Loop over arguments */
- for (argp = args->next; argp != NULL; argp = argp->next) {
- int type = class_key(argp->name);
- if (type == 0) {
- fprintf(stderr, "%s(%s ...) Unknown keyword %s\n",
- macname, mac->macro, argp->name);
- return(1);
- }
- if (type == s_inside) /* Check for inside */
- mac->is_inside = TRUE;
- else
- mactype |= type;
- if (*argp->value) { /* When '=' found, define macro */
- if ((mactype & (s_virtual | s_inline | s_normal)) == 0)
- mactype |= (s_virtual | s_inline | s_normal);
- if ((mactype & (s_private | s_protected | s_public)) == 0)
- mactype |= (s_private | s_protected | s_public);
- { CBODY* body = (CBODY*) getmem(sizeof(CBODY));
- body->name = argp->value;
- body->specifiers = mactype;
- body->next = NULL;
- *next = body;
- next = &body->next;
- mactype = 0;
- }
- }
- }
- if (mactype != 0) {
- fprintf(stderr, "%s(%s ...) macro to call not specified\n",
- macname, mac->macro, argp->name);
- return(1);
- }
- }
- return 0;
- }
-
- /*
- * Instead of malloc'ing for every token, all tokens are kept in
- * work_string, seperated by '\0's. Since work_string may grow,
- * we don't keep pointers into it, but use offsets instead.
- *
- * This macro converts a work-string offset into a char*
- */
- #define WSTRING(n) (work_string->buff+(n))
-
- /*
- * There's a Cmember struct for each member (slot method or friend)
- * of a class. There's a vector of members kept on the stack.
- */
- typedef struct Cmember {
- unsigned int specifiers; /* specifier bit set */
- unsigned int type; /* member type index */
- unsigned int name; /* macro name index */
- unsigned int value; /* Value index if slot */
- unsigned int parms; /* Parameter index if function */
- } CMEMBER;
-
- /*
- * Read in a member (data or function)
- * Fills in tokens with member type, name, parameters/value
- * Fills in delimiters with the delimiter BEFORE the associated token.
- */
- static int class_read(tokens, delimiters, blanks)
- int tokens[20];
- char delimiters[20];
- STRING blanks;
- {
- int ntoken = 0;
- char* token;
- char delim = ' ';
- delimiters[0] = ' ';
-
- for(;;) { /* Loop over tokens in member */
- char c;
- char next_delim;
- blanks->buffp = blanks->buff;
- c = append_blanks(blanks);
- if (c == EOF) break; /* unexpected end of file */
- puts(blanks->buff);
- /* Get next token */
- next_delim = EOS;
- switch (c) {
- case ',':
- if (ntoken > 0) {
- unget(); /* exit and leave comma for next time */
- return ntoken;
- }
- break;
- case ';':
- putchar(c);
- if (ntoken==0) {
- continue; /* }; - ignore ; */
- }
- /* fall through */
- case EOF:
- case '}':
- return ntoken; /* normal return */
- case ':':
- putchar(c);
- delim = c;
- c = append_blanks(blanks);
- puts(blanks->buff);
- break;
- case '<': /* operator<< or parmtype */
- if(!strncmp("operator", WSTRING(tokens[ntoken-1]), 8)) {
- putchar(c);
- work_string->buffp--;
- append_char(work_string, c);
- append_char(work_string, EOS);
- continue;
- } /* else fall through */
- case '[': /* operator[] */
- unget();
- work_string->buffp--;
- delim = (c=='[') ? ']' : '>';
- token = scan_list(delim); /* Append to prevous token */
- puts(token);
- continue;
- case '#': /* #if or something */
- do putchar(c) /* Just copy it */
- while ((c = getchar()) != '\n' && c != EOF);
- putchar(c);
- continue;
- case '(': delim = c; next_delim = ')'; break;
- case '{': delim = c; next_delim = '}'; break;
- case '_': /* Beginning of identifier */
- case '$': next_delim = ' '; break; /* Beginning of identifier */
- }
-
- if (isalnum(c) || next_delim != EOS) {
- unget();
- if (next_delim == EOS) next_delim = ' ';
- token = scan_list(next_delim); /* Get next token */
- puts(token);
- tokens[ntoken] = token - work_string->buff;
- delimiters[ntoken++] = delim;
- } else if (ntoken == 0 && c != '~' && c != ',') {
- fprintf(stderr,"class: Strange character '%c' at beginning of member\n"
- , c);
- return 0;
- } else if (c == ':' && delimiters[ntoken-1] == ':') {
- putchar(c);
- work_string->buffp--; /* :: found, append next token */
- append_STRING(work_string, "::");
- token = scan_list(' ');
- puts(token);
- } else {
- putchar(c);
- tokens[ntoken] = work_string->buffp - work_string->buff;
- delimiters[ntoken++] = c;
- append_char(work_string, EOS);
- next_delim = ' ';
- }
- delim = next_delim;
- if (delim == '}')
- return ntoken; /* End of inline */
- } /* end token loop */
- /* unexpected end of file */
- return ntoken;
- }
-
- unsigned int print_flags(n) /* ***** DEBUG FUNCTION ***** */
- unsigned int n;
- {
- int i;
- for (i=0; i<16; i++)
- if ((1<<i) & n)
- fprintf(stderr, "%s ", classkeys[i]);
- fprintf(stderr, "\n");
- return n;
- }
-
-
- /* Copy body of class */
- static int class_body(members, max_members)
- CMEMBER members[];
- int max_members;
- {
- STRING blanks = make_STRING(80);
- int tokens[20];
- char delimiters[20];
- int protection = s_public;
- int i;
- int nmember;
-
- scan_start();
- *work_string->buffp++ = EOS; /* First char for null string */
-
- for(nmember=0; nmember< max_members; nmember++) { /* Loop over members */
- CMEMBER* m = &members[nmember];
- int start;
- int end;
- int ntoken;
- int member_type = 0;
- m->type = 0;
- m->name = 0;
- m->value = 0;
- m->parms = 0;
- /*
- * Read a member
- */
- ntoken = class_read(tokens, delimiters, blanks);
- if (ntoken == 0) goto last_member;
- if (delimiters[0] == ',') { /* comma means use same type as previous */
- CMEMBER* pm = &members[nmember-1];
- m->type = pm->type;
- m->specifiers = pm->specifiers;
- start = 0;
- } else {
- /*
- * Get keywords at beginning of line
- */
- for (start=0; start<ntoken; start++) { /* Loop over tokens */
- int mask = class_key(WSTRING(tokens[start]));
- if (mask & (s_public | s_private | s_protected)) {
- protection = mask;
- }
- else if (mask & (s_virtual | s_inline | s_normal | s_friend | s_static)){
- member_type |= mask;
- }
- else break;
- } /* Type is normal if isn't unusual */
- if ((member_type&(s_virtual|s_inline|s_normal|s_friend|s_static)) == 0)
- member_type |= s_normal;
- }
- /*
- * What's left is the result-type, name, arglist, body and semicolon.
- * Because we don't know what a type looks like, search BACKWARD
- * for these items. What's left is the result type.
- */
- for (end = ntoken-1; end >= start; end--) {
- switch (delimiters[end]) {
- case '{': /* Body */
- if (member_type & s_methods)
- member_type |= s_inline; /* if a method must be inline */
- ntoken--; /* Don't save body */
- break;
- case ')': /* Parameter list */
- if (end > start) /* If token after parm list */
- *(WSTRING(tokens[end-1])-1) = ' '; /* concatenate parm list */
- break;
- case '(': /* Parameter list */
- m->parms = tokens[end];
- m->name = tokens[--end]; /* Token before parm list is name */
- member_type |= s_methods; /* Must be a method */
- goto exit; /* Everything else is type */
- case '>':
- case ' ': /* slot Name */
- m->name = tokens[end]; /* Token before parm list is name */
- member_type |= s_slots; /* Must be a slot */
- goto exit; /* Everything else is type */
- case '=':
- m->name = tokens[end];
- m->value = tokens[end+1];
- for (i=end+1; i<ntoken; i++)
- *(WSTRING(tokens[i])-1) = ' '; /* concatenate values */
- member_type |= s_slots;
- goto exit;
- case ':': /* field width (ignored) */
- break;
- default: /* debug */
- fprintf(stderr, "class: Strange character \'%c\' found after ",
- delimiters[end]);
- for (i=0; i<end; i++)
- fprintf(stderr, "%c%s",
- delimiters[i], (tokens[i]<0) ? "" : WSTRING(tokens[i]));
- fprintf(stderr, "\n");
- }
- }
- fprintf(stderr, "class: Couldn't find member after "); /* DEBUG */
- fprintf(stderr, "%s%c",
- (tokens[i]<0) ? "" : WSTRING(tokens[i]), delimiters[i]);
- fprintf(stderr, "\n");
- continue;
- exit:
- if (m->type != NULL) continue; /* if comma before member */
- m->type = tokens[start];
- for (i=start+1; i<end; i++) {
- *(WSTRING(tokens[i])-1) = ' '; /* concatenate types */
- if ((*WSTRING(tokens[i])) == EOS)
- *(WSTRING(tokens[i])-1) = delimiters[i];
- }
- m->specifiers = member_type | protection;
- } /* end for token */
- last_member:
- destroy_STRING(blanks);
- if (nmember >= max_members)
- cerror("More than 200 members in a class", "");
- return nmember;
- }
-
- void class_macros(class, base, is_inside, members, nmember)
- char* class;
- char* base;
- int is_inside;
- CMEMBER members[];
- int nmember;
- {
- CMAC* mac;
- char line[200];
- for (mac=Cmac_head; mac != NULL; mac = mac->next) { /* for each macro */
- int n;
- if (mac->is_inside == is_inside) {
- sprintf(line, "\n%s(%s, %s)", mac->macro, class, base); puts(line);
- if (mac->body != NULL) {
- puts(" {\n");
- for(n=0; n<nmember; n++) { /* for each member */
- CMEMBER* m = &members[n];
- CBODY* body;
- unsigned int mspec = m->specifiers;
- for (body=mac->body; body!=NULL; body=body->next) { /* for bodies */
- unsigned int bspec = body->specifiers;
- if ((mspec&bspec&(s_slots|s_methods)) &&
- (mspec&bspec&(s_private|s_protected|s_public)) &&
- (mspec&bspec&(s_virtual|s_inline|s_normal|s_friend|s_static))){
- sprintf(line, "%s(%s, %s, %s)\n",
- body->name, WSTRING(m->type), WSTRING(m->name),
- WSTRING((mspec & s_slots) ? m->value : m->parms));
- puts(line);
- }
- }
- } /* for members in class */
- putchar('}');
- }
- putchar('\n');
- }
- } /* for all macros */
- }
-
- int class_macro(argc, argv)
- int argc;
- char* argv[];
- {
- char c;
- char buff[BSIZE];
- char* classname;
- STRING basename = make_STRING(80);
- if(copytoken(buff) == NULL) /* Skip class keyword */
- return(1);
- puts(buff);
- classname = savestring(scan_next(' ')); /* Get class name */
- putchar(' ');
- puts(classname);
- putchar(' ');
- c = skip_blanks();
- putchar(c);
- switch (c) {
- case '*': /* Type (e.g. extern class foo* bar;) */
- case ';': /* Forward reference */
- { char* buffp = buff;
- char* namep = classname;
- while((c = *namep++) != EOS &&
- (isalpha(c) || c == '_' || c == '$'))
- *buffp++ = c;
- if (c == '<') { /* A template, ensure parmtype is defined */
- extern int parmtype();
- *buffp = EOS;
- new_defmacro(savestring(buff), FALSE, FALSE, '>', '<',
- parmtype, "parmtype", NULL);
- }
- }
- free(classname);
- classname = NULL;
- while ((c = getchar()) != EOF) putchar(c); /* copy rest of input */
- break;
- case '{': /* No base class */
- break;
- case ':':
- { char* base;
- next_base:
- base = scan_next(' '); /* Copy the base class name */
- puts(base);
- if(!strcmp(base, "public") || /* If first word was public, */
- !strcmp(base, "private") || /* or private */
- !strcmp(base, "virtual")) { /* or virtual */
- putchar(' ');
- goto next_base; /* Ignore and get base class name */
- }
- append_STRING(basename, base);
- c = skip_blanks();
- if(c == ',') {
- putchar(c);
- append_char(basename, c);
- goto next_base;
- }
- if(c != '{') {
- fprintf(stderr, "class: Syntax error, '%c' instead of { after base class\n", c);
- return 1;
- }
- }
- putchar(c);
- break;
- default:
- fprintf(stderr, "class: Syntax error, class %s %c\n", classname, c);
- return 1;
- }
- if (classname != NULL) {
- CMEMBER members[200];
- int nmember = class_body(members, 200);
- class_macros(classname, basename->buff, TRUE, members, nmember);/*Inside*/
- puts("};\n"); /* terminate class definition */
- class_macros(classname, basename->buff, FALSE, members,nmember);/*Outside*/
- }
- destroy_STRING(basename);
- return 0;
- }
-